package com.example.grantsys.api;

import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.grantsys.domain.Terminal;
import com.example.grantsys.domain.User;
import com.example.grantsys.prop.JwtProp;
import com.example.grantsys.resp.AuthResp;
import com.example.grantsys.restful.ResultCodeEnum;
import com.example.grantsys.restful.ResultVO;
import com.example.grantsys.service.TerminalService;
import com.example.grantsys.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.util.Date;

@Api(tags = "认证管理")
@RestController
public class AccessAuthApi {

    @Autowired
    private JwtProp jwtProp;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private TerminalService terminalService;
    @Autowired
    private UserService userService;

    /**
     * {
     * "code": 0,
     * "msg": "操作成功",
     * "data": {
     * "account": "aaa",
     * "nikeName": "Jone",
     * "email": "test1@baomidou.com",
     * "age": 18,
     * "clientId": "cloud_web",
     * "uid": "1",
     * "expireDate": "2021-06-07T13:34:54.000+00:00",
     * "canUse": true
     * },
     * "timestamp": 1623065836718
     * }
     *
     * @param accessToken
     * @return
     * @throws UnsupportedEncodingException
     */
    @GetMapping("/authAccess")
    @ApiOperation("token认证")
    public ResultVO<AuthResp> getAuthAccess(@RequestHeader String accessToken) throws UnsupportedEncodingException {
        String tokenKey = "token:" + accessToken;
        if (!stringRedisTemplate.hasKey(tokenKey)) {
            return ResultVO.fail(ResultCodeEnum.TOKEN_IS_INVALID);
        }


        Algorithm algorithm = Algorithm.HMAC256(jwtProp.getSecret());
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer(jwtProp.getIssuer()) //匹配指定的token发布者 auth0
                .build();
        DecodedJWT jwt = verifier.verify(accessToken); //解码JWT ，verifier 可复用
        //签发日期
        Date issueDate = jwt.getIssuedAt();
        //失效日期
        Date expireDate = jwt.getExpiresAt();
        //登录终端
        String clientId = jwt.getClaim("clientId").asString();
        //账户id
        String uid = jwt.getSubject();
        //账号
        String account = jwt.getClaim("account").asString();
        String name = jwt.getClaim("name").asString();
        String email = jwt.getClaim("email").asString();
        Integer age = jwt.getClaim("age").asInt();

        //jwt自身的有效性检查

        if (issueDate == null) {
            return ResultVO.fail(ResultCodeEnum.TOKEN_IS_INVALID);
        }
        if (expireDate == null) {
            return ResultVO.fail(ResultCodeEnum.TOKEN_IS_INVALID);
        }
        Boolean isExpire = DateUtil.isIn(new Date(), issueDate, expireDate);
        if (!isExpire) {
            return ResultVO.fail(ResultCodeEnum.TOKEN_TIME_OUT);
        }

        //client的有效性检查

        if (clientId == null) {
            return ResultVO.fail(ResultCodeEnum.TOKEN_IS_INVALID);
        }
        Terminal terminal = terminalService.getOne(new LambdaQueryWrapper<Terminal>().eq(Terminal::getName, clientId));
        if (terminal == null) {
            return ResultVO.fail(ResultCodeEnum.TOKEN_IS_INVALID);
        }
        if (uid == null) {
            return ResultVO.fail(ResultCodeEnum.TOKEN_IS_INVALID);
        }

        //用户的有效性检查
        User user = userService.getById(uid);
        if (user == null) {
            return ResultVO.fail(ResultCodeEnum.TOKEN_IS_INVALID);
        }
        if (!user.getAccount().equals(account)) {
            return ResultVO.fail(ResultCodeEnum.TOKEN_IS_INVALID);
        }


        return ResultVO.ok(AuthResp.builder()
                .account(account)
                .age(age)
                .canUse(true)
                .clientId(clientId)
                .email(email)
                .expireDate(expireDate)
                .nikeName(name)
                .uid(uid)
                .build());
    }


}
